
#include "logger.h"

bool Logger::Init(const char *filename,
                  int debug_level,
                  bool flush,
                  int rotate_size) {
  if (fp_ != NULL)
    return false;

  if (!OpenFile(filename))
    return false;

  filename_ = filename;
  flush_ = flush;
  rotate_size_ = rotate_size;
  return true;
}

bool Logger::OpenFile(const char *filename) {
  if ((fp_ = fopen(filename, "a")) == NULL) {
    fprintf(stderr, "open log file '%s' fail: %s\n", strerror(errno));
    return false;
  }

  current_size_ = fseek(fp_, 0, SEEK_END);
  if (current_size_ == -1) {
    fprintf(stderr, "seek log file '%s' fail: %s\n", strerror(errno));
    fclose(fp_);
    fp_ = NULL;
    return false;
  }

  struct stat st;
  time_t ctime;
  if (stat(filename, &st) != 0) {
    fprintf(stderr, "stat log file '%s' fail: %s\n", strerror(errno));
    time(&ctime);
  } else {
    ctime = st.st_ctime;
  }
  
  localtime_r(&ctime, &rotate_tm_);

  return true;
}

void Logger::Log(int level, const char *fmt, ...) {
  if (level < debug_level_)
    return;

  char buff[BUFF_SIZE];
  time_t t;
  struct tm tm;
  int buff_len;

  time(&t);  
  localtime_r(&t, &tm);
  
  buff_len = sprintf(buff, "%04d%02d%02d %02d:%02d:%02d ",
                     tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
                     tm.tm_hour, tm.tm_min, tm.tm_sec);


  va_list ap;

  va_start(ap, fmt);
  buff_len += vsnprintf(buff+buff_len, BUFF_SIZE-buff_len, fmt, ap);
  va_end(ap);

  if (buff_len >= BUFF_SIZE) 
    buff_len = BUFF_SIZE - 2;

  buff[buff_len++] = '\n';
  buff[buff_len++] = '\0';

  int wbytes = 0;
  Lock lock(mtx_);
  if ((wbytes = fwrite(buff, 1, buff_len, fp_)) != buff_len) {
    fprintf(stderr, "write log error:%s\n", strerror(errno)); 
    return;
  }

  current_size_ += wbytes; 

  if (flush_) {
    if (fflush(fp_) == EOF) {
      fprintf(stderr, "flush log error:%s\n", strerror(errno)); 
    }
  }

  bool rotate = false;
  if (rotate_size_ > 0 && current_size_ > rotate_size_) {
    rotate = true;
  }

  if (rotate_tm_.tm_year > 0 &&
      (tm.tm_year > rotate_tm_.tm_year ||
       tm.tm_mon > rotate_tm_.tm_mon ||
       tm.tm_mday > rotate_tm_.tm_mday)) {
    rotate = true;
  }

  if (rotate)
    RotateLog();
}

bool Logger::RotateLog() {
  if (filename_.empty())
    return false;

  time_t t;
  struct tm tm;
  char new_filename[PATH_MAX];

  if (fp_) {
    fclose(fp_);
    fp_ = NULL;
  }

  time(&t);
  localtime_r(&t, &tm);

  sprintf(new_filename, "%s.%04d%02d%02d_%02d%02d%02d",
          filename_.c_str(),
          tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
          tm.tm_hour, tm.tm_min, tm.tm_sec);
 
  if (rename(filename_.c_str(), new_filename) != 0) {
    fprintf(stderr, "rename %s to %s fail:%s\n",
            filename_.c_str(), new_filename, strerror(errno));
  }

  return OpenFile(filename_.c_str());
}
